﻿namespace WebPacker.Tool
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;
	using System.Text;
	using WebPacker.Configuration;
	using WebPacker.Helpers;
	using WebPacker.Packs;
	using WebPacker.Packs.Builders;
	using WebPacker.Tool.Sources;

	internal class Program
	{
		private static StringComparison comparison = StringComparison.InvariantCultureIgnoreCase;

		public static int Main(string[] args)
		{
			try
			{
				var cmdArg = new CommandLine(args);

				if (cmdArg.Count == 0 || cmdArg.Contains("?") || cmdArg.Contains("h") || cmdArg.Contains("help"))
				{
					Console.WriteLine("Usage:");
					Console.WriteLine("  WebPackerTool <source> [-config=] [-basePath=] [-output=] [-name=] [-meta=]");
					Console.WriteLine("Options:");
					Console.WriteLine(" source");
					Console.WriteLine("  Set of resource files (separated by a space)");
					Console.WriteLine("  or file with a configuration of packages");
					Console.WriteLine("  or web.config with <web.packer> configuration.");
					Console.WriteLine(" -config=");
					Console.WriteLine("    Path for custom packing configuration file.");
					Console.WriteLine(" -basePath=");
					Console.WriteLine("    Current directory for resources files. Used for resolve \"~\" also. ");
					Console.WriteLine(" -output=");
					Console.WriteLine("    Output path for generated packages.");
					Console.WriteLine(" -name=");
					Console.WriteLine("    Name of package for set of files.");
					Console.WriteLine(" -meta=");
					Console.WriteLine("    File for writing meta information of packages.");
					return 0;
				}

				var files = cmdArg.GetCollection().ToArray();
				var configFile = cmdArg.GetValue("config");
				var packName = cmdArg.GetValue("name", "pack");

				if (files.Length == 0)
				{
					Console.WriteLine("Specify the source for packing.");
					return 0;
				}

				ISource source = null;
				var configuration = Configuration.OpenConfiguration(configFile);

				configuration.Output = cmdArg.GetValue("output");
				configuration.BasePath = cmdArg.GetValue("basePath");
				configuration.MetaFileName = cmdArg.Contains("meta") ? cmdArg.GetValue("meta", "packs.build.xml") : null;

				if (files.Length == 1)
				{
					var file = files[0];

					if (file.EndsWith("web.config", comparison))
					{
						source = new WebConfigSource(configuration, file);
					}
					else if (file.EndsWith(".xml", comparison) || file.EndsWith(".config", comparison))
					{
						source = new PacksSource(configuration, file);
					}
				}

				if (source == null)
				{
					source = new FilesSource(configuration, packName, files);
				}

				source.Open();
				Process(source);
			}
			catch (ToolException e)
			{
				Console.WriteLine(e.Message);
				return -2;
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected error: {0}", e);
				Console.WriteLine(e.StackTrace);
				return -3;
			}

			return 0;
		}

		internal static void Process(ISource source)
		{
			var configuration = source.Configuration;
			var basePath = configuration.BasePath;
			var outputPath = configuration.Output;

			if (string.IsNullOrEmpty(basePath))
			{
				basePath = Directory.GetCurrentDirectory();
			}

			VirtualPathHelper.ApplicationPhysicalPath = basePath;

			if (string.IsNullOrEmpty(outputPath))
			{
				outputPath = Directory.GetCurrentDirectory();
			}
			else
			{
				outputPath = VirtualPathHelper.ToPhisicalPath(outputPath);
			}

				var builder = new PackBuilder(configuration.Filters, null, configuration.VersionGenerators)
			{
				DefaultVersionGenerator = configuration.VersionGeneratorName
			};

			var packFileInfos = new List<PackFileInfo>();

			try
			{
				foreach (var packInfo in source.Packs)
				{
					var pack = builder.BuildPack(packInfo);
					var packFileName = GetFileName(pack);
					var packPath = Path.Combine(outputPath, packFileName);
					File.WriteAllText(packPath, pack.GetContent());
					packFileInfos.Add(new PackFileInfo(pack.PackType, pack.Name, packFileName));
				}
			}
			catch (FileNotFoundException e)
			{
				throw ToolException.FileNotFound(e.FileName);
			}
			catch (IOException e)
			{
				throw new ToolException(e.Message);
			}

			var metaFileName = configuration.MetaFileName;

			if (!string.IsNullOrEmpty(metaFileName))
			{
				if (metaFileName.StartsWith("~"))
				{
					metaFileName = VirtualPathHelper.ToPhisicalPath(configuration.MetaFileName);
				}
				else
				{
					metaFileName = Path.Combine(outputPath, metaFileName);
				}

				PackFileInfo.Serialize(metaFileName, packFileInfos);
			}
		}

		internal static string GetFileName(IPack pack)
		{
			var builder = new StringBuilder(pack.Name.Length + 10);
			builder.Append(pack.Name);

			if (!string.IsNullOrEmpty(pack.Version))
			{
				builder.Append('.').Append(pack.Version);
			}

			if (pack.PackType == PackType.JavaScript)
			{
				builder.Append(".js");
			}
			else if (pack.PackType == PackType.Css)
			{
				builder.Append(".css");
			}

			return builder.ToString();
		}
	}
}
